use tokio::sync::oneshot;
use tonic::transport::Channel;

use super::iblog_proto::{self, iblog_client::IblogClient};
use crate::types::{self, channel};

pub struct Client {
    client: IblogClient<Channel>,
}

impl Client {
    pub async fn new() -> Self {
        // server is a micro serviec: iblog
        // port is 9001, and k8s dns will handle this domain
        // "http" is necessary
        let iblog_channel = Channel::from_static("http://iblog:9001")
            .connect()
            .await
            .expect("failed to connect to grpc server in iblog");

        println!("Connect to iblog grpc server successfully");

        Client {
            client: IblogClient::new(iblog_channel),
        }
    }

    pub async fn blog_add(
        &mut self,
        request: types::blog::AddRequest,
        rsp_ch: oneshot::Sender<channel::Response>,
    ) {
        println!("blog_add: {:?}", &request);
        match self
            .client
            .blog_add(iblog_proto::Blog {
                id: 0,
                title: request.title,
                content: request.content,
                tags: request.tags,
                author: request.author,
                author_id: request.author_id,
                created_at: "".to_string(), // Automatically generated by db
                views: 0,
                stars: 0,
            })
            .await
        {
            Ok(rsp) => {
                let blog_id = rsp.into_inner().value;
                rsp_ch
                    .send(channel::Response::BlogAdd(blog_id))
                    .expect("Grpc send blog-add response failed");
            }
            Err(status) => rsp_ch
                .send(channel::Response::Error(status.to_string()))
                .expect("Grpc send blog-add response failed"),
        }
    }

    pub async fn blog_update(
        &mut self,
        request: types::blog::UpdateRequest,
        rsp_ch: oneshot::Sender<channel::Response>,
    ) {
        let mut title = "".to_string();
        let mut content = "".to_string();
        let mut tags: Vec<String> = vec![];

        if let Some(title_new) = request.title {
            title = title_new;
        }

        if let Some(content_new) = request.content {
            content = content_new;
        }

        if let Some(tags_new) = request.tags {
            tags = tags_new;
        }

        match self
            .client
            .blog_update(iblog_proto::Blog {
                id: request.id,
                title,
                content,
                tags,
                author: "".to_string(),
                author_id: "".to_string(),
                created_at: "".to_string(),
                views: 0,
                stars: 0,
            })
            .await
        {
            Ok(_) => rsp_ch
                .send(channel::Response::Ok())
                .expect("Grpc send blog-update response failed"),

            Err(status) => rsp_ch
                .send(channel::Response::Error(status.to_string()))
                .expect("Grpc send blog-update response failed"),
        }
    }

    pub async fn blog_get(&mut self, id: u32, rsp_ch: oneshot::Sender<channel::Response>) {
        match self.client.blog_get(iblog_proto::Id { value: id }).await {
            Ok(rsp) => {
                let blog = rsp.into_inner();
                rsp_ch
                    .send(channel::Response::BlogGet(types::blog::Blog {
                        id: blog.id,
                        title: blog.title,
                        content: blog.content,
                        author: blog.author,
                        author_id: blog.author_id,
                        tags: blog.tags,
                        created_at: blog.created_at,
                        views: blog.views,
                        stars: blog.stars,
                    }))
                    .expect("Grpc send blog-get response failed");
            }
            Err(status) => rsp_ch
                .send(channel::Response::Error(status.to_string()))
                .expect("Grpc send blog-get response failed"),
        }
    }

    pub async fn blogs_get(
        &mut self,
        request: types::blog::GetMutipleRequest,
        rsp_ch: oneshot::Sender<channel::Response>,
    ) {
        match self
            .client
            .blogs_get(iblog_proto::BlogsGetRequest {
                id: request.id,
                count: request.count,
            })
            .await
        {
            Ok(rsp) => {
                let blogs_info = rsp.into_inner();
                rsp_ch
                    .send(channel::Response::BlogsGet(
                        types::blog::GetMutipleResponse {
                            total_count: blogs_info.total_count,
                            blog_count: blogs_info.blog_count,
                            blogs: blogs_info
                                .blogs
                                .into_iter()
                                .map(|blog| types::blog::Blog {
                                    id: blog.id,
                                    title: blog.title,
                                    content: blog.content,
                                    author: blog.author,
                                    author_id: blog.author_id,
                                    tags: blog.tags,
                                    created_at: blog.created_at,
                                    views: blog.views,
                                    stars: blog.stars,
                                })
                                .collect(),
                        },
                    ))
                    .expect("Grpc send blogs-get response failed");
            }
            Err(status) => rsp_ch
                .send(channel::Response::Error(status.to_string()))
                .expect("Grpc send blogs-get response failed"),
        }
    }

    pub async fn blog_delete(&mut self, id: u32, rsp_ch: oneshot::Sender<channel::Response>) {
        match self.client.blog_delete(iblog_proto::Id { value: id }).await {
            Ok(_) => rsp_ch
                .send(channel::Response::Ok())
                .expect("Grpc send blog-delete response failed"),
            Err(status) => rsp_ch
                .send(channel::Response::Error(status.to_string()))
                .expect("Grpc send blog-delete response failed"),
        }
    }

    pub async fn blog_view_inc(&mut self, id: u32, rsp_ch: oneshot::Sender<channel::Response>) {
        match self
            .client
            .blog_view_inc(iblog_proto::Id { value: id })
            .await
        {
            Ok(rsp) => {
                let views = rsp.into_inner().value;
                rsp_ch
                    .send(channel::Response::Views(views))
                    .expect("Grpc send blog-view-inc response failed");
            }
            Err(status) => rsp_ch
                .send(channel::Response::Error(status.to_string()))
                .expect("Grpc send blog-view-inc response failed"),
        }
    }

    pub async fn blog_star_inc(&mut self, id: u32, rsp_ch: oneshot::Sender<channel::Response>) {
        match self
            .client
            .blog_star_inc(iblog_proto::Id { value: id })
            .await
        {
            Ok(rsp) => {
                let stars = rsp.into_inner().value;
                rsp_ch
                    .send(channel::Response::Views(stars))
                    .expect("Grpc send blog-star-inc response failed");
            }
            Err(status) => rsp_ch
                .send(channel::Response::Error(status.to_string()))
                .expect("Grpc send blog-star-inc response failed"),
        }
    }

    pub async fn blog_star_dec(&mut self, id: u32, rsp_ch: oneshot::Sender<channel::Response>) {
        match self
            .client
            .blog_star_dec(iblog_proto::Id { value: id })
            .await
        {
            Ok(rsp) => {
                let stars = rsp.into_inner().value;
                rsp_ch
                    .send(channel::Response::Views(stars))
                    .expect("Grpc send blog-star-dec response failed");
            }
            Err(status) => rsp_ch
                .send(channel::Response::Error(status.to_string()))
                .expect("Grpc send blog-star-dec response failed"),
        }
    }

    pub async fn idea_add(
        &mut self,
        request: types::idea::AddRequest,
        rsp_ch: oneshot::Sender<channel::Response>,
    ) {
        match self
            .client
            .idea_add(iblog_proto::Idea {
                id: 0,
                title: request.title,
                content: request.content,
                tags: request.tags,
                created_at: "".to_string(), // Automatically generated by db
                views: 0,
                stars: 0,
            })
            .await
        {
            Ok(rsp) => {
                let idea_id = rsp.into_inner().value;
                rsp_ch
                    .send(channel::Response::IdeaAdd(idea_id))
                    .expect("Grpc send idea-add response failed");
            }
            Err(status) => rsp_ch
                .send(channel::Response::Error(status.to_string()))
                .expect("Grpc send idea-add response failed"),
        }
    }

    pub async fn idea_update(
        &mut self,
        request: types::idea::UpdateRequest,
        rsp_ch: oneshot::Sender<channel::Response>,
    ) {
        let mut title = "".to_string();
        let mut content = "".to_string();
        let mut tags: Vec<String> = vec![];

        if let Some(title_new) = request.title {
            title = title_new;
        }

        if let Some(content_new) = request.content {
            content = content_new;
        }

        if let Some(tags_new) = request.tags {
            tags = tags_new;
        }

        match self
            .client
            .idea_update(iblog_proto::Idea {
                id: 0,
                title,
                content,
                tags,
                created_at: "".to_string(), // Automatically generated by db
                views: 0,
                stars: 0,
            })
            .await
        {
            Ok(_) => rsp_ch
                .send(channel::Response::Ok())
                .expect("Grpc send idea-update response failed"),
            Err(status) => rsp_ch
                .send(channel::Response::Error(status.to_string()))
                .expect("Grpc send idea-update response failed"),
        }
    }

    pub async fn idea_get(&mut self, id: u32, rsp_ch: oneshot::Sender<channel::Response>) {
        match self.client.idea_get(iblog_proto::Id { value: id }).await {
            Ok(rsp) => {
                let idea = rsp.into_inner();
                rsp_ch
                    .send(channel::Response::IdeaGet(types::idea::Idea {
                        id: idea.id,
                        title: idea.title,
                        content: idea.content,
                        tags: idea.tags,
                        created_at: idea.created_at,
                        views: idea.views,
                        stars: idea.stars,
                    }))
                    .expect("Grpc send idea-get response failed");
            }
            Err(status) => rsp_ch
                .send(channel::Response::Error(status.to_string()))
                .expect("Grpc send idea-get response failed"),
        }
    }

    pub async fn ideas_get(
        &mut self,
        request: types::idea::GetMutipleRequest,
        rsp_ch: oneshot::Sender<channel::Response>,
    ) {
        match self
            .client
            .ideas_get(iblog_proto::IdeasGetRequest {
                id: request.id,
                count: request.count,
            })
            .await
        {
            Ok(rsp) => {
                let ideas_info = rsp.into_inner();
                rsp_ch
                    .send(channel::Response::IdeasGet(
                        types::idea::GetMutipleResponse {
                            total_count: ideas_info.total_count,
                            idea_count: ideas_info.idea_count,
                            ideas: ideas_info
                                .ideas
                                .into_iter()
                                .map(|idea| types::idea::Idea {
                                    id: idea.id,
                                    title: idea.title,
                                    content: idea.content,
                                    tags: idea.tags,
                                    created_at: idea.created_at,
                                    views: idea.views,
                                    stars: idea.stars,
                                })
                                .collect(),
                        },
                    ))
                    .expect("Grpc send ideas-get response failed");
            }
            Err(status) => rsp_ch
                .send(channel::Response::Error(status.to_string()))
                .expect("Grpc send ideas-get response failed"),
        }
    }

    pub async fn idea_delete(&mut self, id: u32, rsp_ch: oneshot::Sender<channel::Response>) {
        match self.client.idea_delete(iblog_proto::Id { value: id }).await {
            Ok(_) => rsp_ch
                .send(channel::Response::Ok())
                .expect("Grpc send idea-delete response failed"),
            Err(status) => rsp_ch
                .send(channel::Response::Error(status.to_string()))
                .expect("Grpc send idea-delete response failed"),
        }
    }

    pub async fn idea_view_inc(&mut self, id: u32, rsp_ch: oneshot::Sender<channel::Response>) {
        match self
            .client
            .idea_view_inc(iblog_proto::Id { value: id })
            .await
        {
            Ok(rsp) => {
                let views = rsp.into_inner().value;
                rsp_ch
                    .send(channel::Response::Views(views))
                    .expect("Grpc send idea-view-inc response failed");
            }
            Err(status) => rsp_ch
                .send(channel::Response::Error(status.to_string()))
                .expect("Grpc send idea-view-inc response failed"),
        }
    }

    pub async fn idea_star_inc(&mut self, id: u32, rsp_ch: oneshot::Sender<channel::Response>) {
        match self
            .client
            .idea_star_inc(iblog_proto::Id { value: id })
            .await
        {
            Ok(rsp) => {
                let stars = rsp.into_inner().value;
                rsp_ch
                    .send(channel::Response::Views(stars))
                    .expect("Grpc send idea-star-inc response failed");
            }
            Err(status) => rsp_ch
                .send(channel::Response::Error(status.to_string()))
                .expect("Grpc send idea-star-inc response failed"),
        }
    }

    pub async fn idea_star_dec(&mut self, id: u32, rsp_ch: oneshot::Sender<channel::Response>) {
        match self
            .client
            .idea_star_dec(iblog_proto::Id { value: id })
            .await
        {
            Ok(rsp) => {
                let stars = rsp.into_inner().value;
                rsp_ch
                    .send(channel::Response::Views(stars))
                    .expect("Grpc send idea-star-dec response failed");
            }
            Err(status) => rsp_ch
                .send(channel::Response::Error(status.to_string()))
                .expect("Grpc send idea-star-dec response failed"),
        }
    }
}
